<?php

namespace App\Services\Product;

use App\Exceptions\ChargeRecordException;
use App\Exceptions\DeviceException;
use App\Exceptions\UserException;
use App\Facades\ChargeRecordManager;
use App\Facades\ConfigService as LocalConfigService;
use App\Facades\DeviceManager;
use App\Facades\UserManager;
use App\Jobs\CommandRepeater;
use App\Models\ChargeRecord;
use Cache;
use Carbon\Carbon;
use ChengFang\EasyPay\PaymentContext;
use DB;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Redis;
use Request;

class ChargeRecordService {
    use \ChengFang\EasyPay\Traits\WechatPayNotify, DispatchesJobs;

    /**
     * 充值，微信公众号内支付
     * @param  int  $money  金额
     * @return 
     */
    public function wechatUnifiedPay($flow, $money){
        $userInfo = UserManager::getUser()->userInfo;
        $deviceState = $this->getCacheDeviceState($userInfo->current_device_id);

        $goldAmount = round($money);
        $orderNumber = date('YmdHis').substr(microtime(), 2, 4);
        $chargeRecord = ChargeRecordManager::createOrder([
            'order_number' => $orderNumber,
            'user_id' => $userInfo->user_id,
            'open_id' => $userInfo->open_id,
            'device_id' => $userInfo->current_device_id,
            'money' => number_format($money, 2, '.', ''),
            "residual_flow" => $deviceState['residualFlow'] + $flow,
            'flow' => $flow,
        ]);

        $order = [
            'body' => $flow.'升',
            'detail' => $flow.'升',
            'out_trade_no' => $orderNumber,
            'total_fee' => round($money) * 100,
            'spbill_create_ip' => Request::ip(),
            'notify_url' => url('/api/wechat-pay-notify'),
            'openid' => $userInfo->open_id,
        ];
        
        $context = new PaymentContext('Strategy\WechatPay\WechatJsPay');
        $context->execute($order);
        // info($context->getResult()->createWebPaymentPackage());
        return $context->getResult()->createWebPaymentPackage();
        
    }

    /**
     * 充值，微信扫码支付
     * @param  integer $money
     * @return [type]
     */
    public function wechatJsPay($flow, $money){
        $userInfo = UserManager::getUser()->userInfo;
        $deviceState = $this->getCacheDeviceState($userInfo->current_device_id);

        $goldAmount = round($money);
        $orderNumber = date('YmdHis').substr(microtime(), 2, 4);
        $chargeRecord = ChargeRecordManager::createOrder([
            'order_number' => $orderNumber,
            'user_id' => $userInfo->user_id,
            'open_id' => $userInfo->open_id,
            'device_id' => $userInfo->current_device_id,
            'money' => number_format($money, 2, '.', ''),
            "residual_flow" => $deviceState['residualFlow'] + $flow,
            'flow' => $flow,
        ]);

        $order = [
            'body' => $flow.'升',
            'detail' => $flow.'升',
            'out_trade_no' => $orderNumber,
            // 'total_fee' => round($money) * 100,
            'product_id' => $orderNumber,
            'spbill_create_ip' => Request::ip(),
            'notify_url' => url('/api/wechat-pay-notify'),
        ];

        $context = new PaymentContext('Strategy\WechatPay\WechatUnifiedPay');
        $context->execute($order);

        return $context->getResult()->getCodeUrl();
    }

    /**
     * 充值，微信回调，增加用户升数量
     * @param  [type] $body 
     * @return [type]       
     */
    public function recharge($body){
        try{
            $context = new PaymentContext('Strategy\WechatPay\WechatPayNotify');
            $context->execute($body);
            $response = $context->getResult();
            $orderNumber = $response->getOutTradeNo();

            $chargeRecord = ChargeRecordManager::findByOrderNumber($orderNumber);

            if($chargeRecord->status == ChargeRecord::ORDER_STATUS_PAID){
                return $this->success();
            }

            DB::transaction(function() use ($chargeRecord) {
                $chargeRecord->update(['status' => ChargeRecord::ORDER_STATUS_PAID]);
                $deviceCode = $chargeRecord->device->code;
                switch(substr($deviceCode, 0, 2))
                {
                    case '20':
                        $job = (new CommandRepeater('recharge', $chargeRecord->flow, $deviceCode, $chargeRecord->id, $chargeRecord->user_id))->onQueue('commandRepeater');
                        break;
                    case '21':
                        $job = (new CommandRepeater('mode_recharge', $chargeRecord->flow, $deviceCode, $chargeRecord->id, $chargeRecord->user_id, false, $chargeRecord->recharge_mode))->onQueue('commandRepeater');
                        break;
                }
                $this->dispatch($job);
            });
            return $this->success();
        }catch(Exception $e){
            info(['ChargeRecordService.recharge' => $body]);
            return $this->fail();
        }
    }

    /**
     * 获取金额和金币数量数组
     * @return [type] 
     */
    public function getCharge(){
        $charges = config('heting.rechargeScale');
        if(!count($charges)){
            throw new ChargeRecordException('notFound');
        }
        return $charges;
    }

    /**
     * 添加充值记录
     * @param integer $money     
     * @param integer $quantity     
     * @param integer $deviceId 
     * @param float   $price 
     * @param string  $remark
     * @param array   $newColumn    mode 续费模式
     */
    public function add($money, $quantity, $deviceId, $remark, $status, $price, $newColumn = ['mode' => 2])
    {
        $user = UserManager::getUser();
        if(!$user->hasAccess(['finance'])){
            throw new UserException('auth');
        }

        if($user->is_admin){
            $deviceState = $this->getCacheDeviceState($deviceId, false);
            $orderArray = [
                'order_number' => date('YmdHis').substr(microtime(), 2, 4),
                'admin_id' => $user->id,
                'device_id' => $deviceId,
                'money' => $money,
                'status' => $status,
                'price' => $price,
                'remark' => $remark,
                'recharge_mode' => $newColumn['mode'],
            ];
            switch($newColumn['mode']){
                case 1:
                    $orderArray = array_merge($orderArray, [
                        'days' => $quantity,
                        "time_limit" => $status == ChargeRecord::ORDER_STATUS_OFFLINE_RECHARGE? 0: $deviceState['timeLimit'] + $quantity,
                        "residual_flow" => $status == ChargeRecord::ORDER_STATUS_OFFLINE_RECHARGE? 0: $deviceState['residualFlow']
                        ]);
                    break;
                case 2:
                    $orderArray = array_merge($orderArray, [
                        'flow' => $quantity,
                        "residual_flow" => $status == ChargeRecord::ORDER_STATUS_OFFLINE_RECHARGE? 0: $deviceState['residualFlow'] + $quantity,
                        "time_limit" => $status == ChargeRecord::ORDER_STATUS_OFFLINE_RECHARGE? 0: $deviceState['timeLimit']
                        ]);
                    break;
            }

            $chargeRecord = ChargeRecordManager::createOrder($orderArray);
            return $chargeRecord;
        }
        
    }

    /**
     * 获取充值记录列表
     * @param  string $condition 
     * @return              
     */
    public function findChargeList($condition)
    {
        $chargeRecords = ChargeRecordManager::findChargeListByQuery($condition);
        return $chargeRecords->with(['device' => function($query){
            $query->with(['installInfo' => function($query){
                $query->select('device_code', 'customer_name');
            }])->select('id', 'code');
        }, 'userInfo' => function($query){
            $query->select('user_id', 'name');
        }, 'adminInfo' => function($query){
            $query->select('user_id', 'name');
        }, 'dischargeAdmin' => function($query){
            $query->select('user_id', 'name');
        }])->paginate(15);
    }

    /**
     * 根据设备 id 获取设备缓存数据
     * @param  integer  $deviceId 
     * @return            
     */
    public function getCacheDeviceState($deviceId, $throw = true)
    {
        $device = DeviceManager::getDeviceByQuery('id', $deviceId);
        $deviceState = Cache::get($device->code.'state');
        if(is_null($deviceState) && $throw){
            throw new DeviceException('notConnect');
        }
        return $deviceState;
    }

    /**
     * 根据设备 id 获取设备列表
     * @param  integer  $deviceId   
     * @param  integer  $userId     
     * @param  boolean $isPaginate 
     * @return               
     */
    public function getChargeListByDeviceId($deviceId = null, $userId = null, $isPaginate = true)
    {
        $chargeRecords = ChargeRecordManager::getChargeListByQueryColumn('device_id', $deviceId)->where('status', '>', ChargeRecord::ORDER_STATUS_WAIT_PAYMENT);
        $query = $chargeRecords->with(['device' => function($query){
            $query->with(['installInfo' => function($query){
                $query->select('device_code', 'customer_name');
            }])->select('id', 'code');
        }, 'userInfo' => function($query){
            $query->select('user_id', 'name', 'phone');
        }, 'adminInfo' => function($query){
            $query->select('user_id', 'name');
        }, 'dischargeAdmin' => function($query){
            $query->select('user_id', 'name');
        }])->latest();
        if($isPaginate){
            return $query->paginate(15);
        }else{
            return $query->select([
                'discharge_flow',
                'flow',
                'money',
                'status',
                'created_at',
                'price',
                'days',
                'discharge_time',
                'recharge_mode',
                ])->get();
        }
    }

    public function updateChargeRecord($chargeRecordId, $data, $rechargeMode)
    {
        $chargeRecord = ChargeRecordManager::getChageRecordById($chargeRecordId);
        // 是否为可撤销状态
        if($chargeRecord->status != ChargeRecord::ORDER_STATUS_RECHARGE){
            if($chargeRecord->status == ChargeRecord::ORDER_STATUS_DISCHARGED){
                throw new ChargeRecordException('discharged');
            }
            throw new ChargeRecordException('canNotDischarge');
        }
        if($rechargeMode != $chargeRecord->recharge_mode){
            throw new ChargeRecordException('reduce');
        }

        $device = DeviceManager::getDeviceByQuery('id', $chargeRecord->device_id);
        $state = Cache::get($device->code.'state');
        $mode = key_exists('mode', $data)? $data['mode']: 2;
        switch($mode)
        {
            case 1:
                if($data['discharge_time'] > $chargeRecord->days || $data['discharge_time'] > $chargeRecord->time_limit){
                    throw new ChargeRecordException('reduceTime');
                }
                $data = array_merge($data, ['time_limit' => $state['timeLimit'] - $data['discharge_time']]);
                break;
            case 2:
                if($data['discharge_flow'] > $chargeRecord->flow || $data['discharge_flow'] > $chargeRecord->residual_flow){
                    throw new ChargeRecordException('reduceFlow');
                }
                if(round($data['discharge_money']/$chargeRecord->price) != $data['discharge_flow']){
                    throw new ChargeRecordException('price');
                }
                $data = array_merge($data, ['residual_flow' => $state['residualFlow'] - $data['discharge_flow']]);
                break;
        }

        $chargeRecord->update($data);
        return $chargeRecord;
    }

    public function getChargeListByDateAndDealer($afterDate, $beforeDate, $dealer, $isPaginate = true)
    {
        $chargeRecordBuilder = ChargeRecordManager::getChargeBuilderOrderByCreatedAt();
        if(!empty($afterDate)){
            $beforeDate = Carbon::createFromFormat('Y-m-d H:i:s', $beforeDate.' 23:59:59');
            $afterDate = Carbon::createFromFormat('Y-m-d H:i:s', $afterDate.' 00:00:00');
            if($beforeDate->lt($afterDate)){
                throw new ChargeRecordException('date');
            }
            $chargeRecordBuilder->where('created_at', '>=', $afterDate)->where('created_at', '<=', $beforeDate);
        }
        if(!empty($dealer)){
            $installInfoBuilder = DeviceManager::getInstallInfoListBuilderOrderByCreatedAt();
            $installs = $installInfoBuilder->where('dealer', 'like', '%'.$dealer.'%')->select('device_code')->get();
            $deviceIds = DeviceManager::getDevicesByDeviceCodeArray($installs->pluck('device_code'));
            $chargeRecordBuilder->whereIn('device_id', $deviceIds->pluck('id'))->with(['device' => function($query){
                $query->with(['installInfo' => function($query){
                    $query->select('device_code', 'dealer', 'customer_name');
                }])->select('id', 'code');
            }, 'userInfo' => function($query){
                $query->select('user_id', 'name');
            }, 'adminInfo' => function($query){
                $query->select('user_id', 'name');
            }, 'dischargeAdmin' => function($query){
                $query->select('user_id', 'name');
            }]);
        }
        if($isPaginate){
            return $chargeRecordBuilder->where('status', '>', ChargeRecord::ORDER_STATUS_PAID)->paginate(15);
        }else{
            return $chargeRecordBuilder->where('status', '>', ChargeRecord::ORDER_STATUS_PAID)->get();
        }
    }

    public function getChargeListByStatus($status)
    {
        $chargeRecords = ChargeRecordManager::getChargeListByQueryColumn('status', $status);
        $query = $chargeRecords->with(['device' => function($query){
            $query->select('id', 'code');
        },  'adminInfo' => function($query){
            $query->select('user_id', 'name');
        }])->latest();
        return $query->select([
            'id',
            'device_id',
            'discharge_flow',
            'flow',
            'money',
            'status',
            'admin_id',
            'created_at',
            'price',
            'recharge_mode',
            'time_limit',
            'days',
            ])->get();
    }

    public function getPrice()
    {
        $price = Redis::get('price');
        if(is_null($price)){
            $price = config('heting.default_price');
        }
        return $price;
    }

    public function setDischargeMoneyAndMoney()
    {
        try{
            $charges = ChargeRecordManager::getChargeBuilderOrderByCreatedAt()->get();
            foreach ($charges as $charge) {
                if(!is_null($charge->discharge_flow)){
                    $charge->update(['discharge_money' => $charge->discharge_flow * $charge->price]);
                }
                if($charge->money == 0){
                    $charge->update(['money' => $charge->flow * $charge->price]);
                }
            }
        }catch(Exception $e){
            dd($e->getMessage());
        }
    }

}
